library ScriptEngine;

uses sysutils, CoreI, lua, LuaUtils, common, ScriptEngineI, ResourceManagerI, SoundSystemI, ConsoleI, sdl, VectorGeometry, SpeechI;

type PLoadedScript=^TLoadedScript;
TLoadedScript=record
buf:pointer;
size:cardinal;
end;

var l:Lua_State=nil;
ResourceManager:IResourceManager=nil;

function LuaPanic(l:lua_state):integer; cdecl;
begin
Core_Error(PChar('Lua Panic: ' + luatostring(l, -1)), ECore_Main);
result:=0;
end;

procedure ExecString(const str,Where:PChar); cdecl;
begin
try
luaLoadBuffer(l, str, where);
luaPCall(l,0,0,0);
except
on e:Exception do Core_Error(PChar(e.Message), ECore_Main);
end;
end;

procedure ExecResource(const name:PChar); cdecl;
var p: Pointer;
begin
p:=ResourceManager^.GetResource(name, rt_Script);
if (P = nil) or (ResourceManager^.GetResourceType(p) <> rt_script) then begin
Core_Error(PChar('ScriptEngine.ExecResource: resource "' +name + '" not found or is not a valid script resource'), ECore_Main);
exit;
end;
if lual_loadbuffer(l, PLoadedScript(ResourceManager^.GetResourceData(p))^.buf, PLoadedScript(ResourceManager^.GetResourceData(p))^.size, name) <> 0 then begin
Core_Error(PChar(LuaStackToStr(L, -1, -1)), ECore_Main);
exit;
end;
                                          try
                                              luaPCall(l,0,0,0);
except
on e:Exception do core_Error(PChar(e.Message), ECore_Main);
end;
                                                  end;

function InitLuaScript(const RawData:pointer; const RawDataSize:cardinal; var FreeRawData:boolean; const name:PChar):pointer; cdecl;
function mywriter(L: lua_State; p: Pointer; sz: Cardinal; const ud: PLoadedScript): Integer; cdecl;
begin
inc(ud^.size, sz);
try
reallocmem(ud^.buf, ud^.size);
except
on e:exception do begin
log_write(PChar('Exception while compiling script resource "'+name+'". '+e.Message), ll_error);
result:=1;
exit;
end;
end;
move(p^, pointer(cardinal(ud^.buf)+ud^.size - sz)^, sz);
result:=0;
end;
var ls:PLoadedScript;
begin
result:=nil;
if lual_loadbuffer(l, RawData, RawDataSize, 'Script')<>0 then begin
Core_Error(PChar(LuaStackToStr(L, -1, -1)), ECore_Main);
exit;
end;
new(ls);
fillchar(ls^, sizeof(TLoadedScript), 0);
getmem(ls^.buf, 12);
                   lua_dump(l, @mywriter, ls);
lua_pop(l,1);
result:=ls;
FreeRawData := true;
end;

Procedure FreeLuaScript(data:pointer); cdecl;
begin
freemem(PLoadedScript(data)^.buf);
freemem(data);
end;
Function GetState:lua_state; cdecl;
          begin
Core_RequirePlugin(ScriptEngineI.ScriptEngineGUID);
          result:=l;
          end;

          {$i loop.inc}
                    Function SE_EventHandler(event:TSDL_Event):boolean; cdecl;
begin
case event.user.code of
SE_Event_ExecString: begin
Core_RegisterErrorHandler(PSE_ExecStringStruct(event.user.data1)^.ErrorHandler);
ExecString(PSE_ExecStringStruct(event.user.data1)^.chunk, PSE_ExecStringStruct(event.user.data1)^.where);
Core_UnregisterErrorHandler(PSE_ExecStringStruct(event.user.data1)^.ErrorHandler);
end;
end;
result:=false;
end;

{$i LuaBindings.inc}
    var PluginInfo:PPluginInfo=nil;
procedure Init; cdecl;
begin
ResourceManager:=Core_RequirePlugin(ResourceManagerI.ResourceManagerGUID);
ResourceManager^.AddResourceHandler(RT_Script, InitLuaScript, FreeLuaScript);
                    if                LoadLuaLib <= 0 then begin
Core_Error('Can not load Lua library', Ecore_Init);
exit;
end;
l := lual_newstate;
if l = nil then begin
Core_Error('Unable to create Lua descriptor', ECore_Init);
exit;
end;
lua_atpanic(l,luapanic);
luaJIT_setmode(l,0,LUAJIT_MODE_ENGINE or LUAJIT_MODE_ON);
lua_pushcfunction(l, luaopen_base);
lua_call(l, 0,1);
lua_pushcfunction(l, luaopen_package);
lua_call(l, 0,1);
lua_pushcfunction(l, luaopen_math);
lua_call(l, 0,1);
lua_pushcfunction(l, luaopen_table);
lua_call(l, 0, 1);
//loop initialization
  luaL_findtable(L, LUA_GLOBALSINDEX, 'package.preload', 3);
	lua_pushcfunction(L, luaopen_loop_base);
	lua_setfield(L, -2, 'loop.base');
	lua_pushcfunction(L, luaopen_loop_simple);
	lua_setfield(L, -2, 'loop.simple');
	lua_pushcfunction(L, luaopen_loop_table);
	lua_setfield(L, -2, 'loop.table');
	lua_pushcfunction(L, luaopen_loop_collection_ObjectCache);
	lua_setfield(L, -2, 'loop.collection.ObjectCache');
	lua_pop(L, 1);
  RegisterBindings;
Core_RegisterEventHandler(Event_ScriptEngine, Se_EventHandler);
  end;
  Procedure LuaInit; cdecl;
  begin
  end;

  Procedure LuaFree; cdecl;
begin
dispose(ILua(PPluginInfo(cardinal(PluginInfo) + sizeof(TPluginInfo))^.InterfaceStruct));
freemem(PluginInfo);
                    end;
Procedure Free; cdecl;
begin
  lua_close(L);
    l := nil;
    Core_UnregisterEventHandler(SE_EventHandler);
    dispose(IScriptEngine(PluginInfo^.interfaceStruct));
    SoundSystem:=nil;
ResourceManager:=nil;
    FreeLuaLib;
end;

Procedure Plugin_GetInfo(var info:PPluginInfo; var length:cardinal); cdecl;
begin
length:=2;
GetMem(info, sizeof(TPluginInfo) * length);
PluginInfo:=Info;
info^.Name := 'ScriptEngine_Lua';
info^.GUID := ScriptEngineGUID;
Info^.init := init;
info^.free := free;
new(IScriptEngine(Info^.InterfaceStruct));
IScriptEngine(Info^.InterfaceStruct)^.ExecString := ExecString;
IScriptEngine(Info^.InterfaceStruct)^.ExecResource := ExecResource;
info:=pointer(cardinal(info) + sizeof(TPluginInfo));
fillchar(info^, sizeof(TPluginInfo), 0);
info^.GUID := LuaGUID;
info^.Init := LuaInit;
info^.free := LuaFree;
info^.Name := 'Lua interface';
new(ILua(info^.InterfaceStruct));
ILua(info^.InterfaceStruct)^.GetState := GetState;
ILua(Info^.InterfaceStruct)^.LuaToVector3F := LuaToVector3F;
ILua(Info^.InterfaceStruct)^.LuaPushVector3F := LuaPushVector3F; 
ILua(Info^.InterfaceStruct)^.LuaToSoundHandle := LuaToSoundHandle;
ILua(Info^.InterfaceStruct)^.LuaPushSoundHandle := LuaPushSoundHandle;   
info:=PluginInfo;
end;

exports Plugin_GetInfo;
begin
end.
